package rebalance;

import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

import node.NodeManager;

import remote.RemoteDistributedHashMap;
import log.Logger;
import messaging.InterNodeMessage;

public class RebalanceChecks
{
  /**
   * Node number for which an exception is thrown while trying to send a message to.
   */
  public int dead_node_number;
  /**
   * A reference of node manager.
   */
  private NodeManager nm;
  /**
   * The finding node number.
   */
  private int my_node_num;
  /**
   * Method name to re-balance.
   */
  private int REBALANCE_METHOD = 4;
  /**
   * Constructor.
   */
  public RebalanceChecks(NodeManager nodeman,int my_node, int missing_node_num)
  {
    this.dead_node_number = missing_node_num;
    this.my_node_num = my_node;
    this.nm = nodeman;
  }
  /**
   * find the predecessor actual node  of this node.
   * @param node_num - the node number for which the predecessor actual node is found.
   * @return int - the predecessor actual node for this node number.
   */
  public int findPredecessor(final int node_num) 
  {
    int index = node_num - 1;
    if (index == NodeManager.total_nodes_chord)
    {
      index = 0;
    }
    if (index < 0)
    {
      index = NodeManager.total_nodes_chord - 1;
    }
    while (nm.rmi_NodeFind(index).endsWith("V"))
    {
      index--;
      if (index == NodeManager.total_nodes_chord)
      {
        index = 0;
      }
      if (index < 0)
      {
        index = NodeManager.total_nodes_chord - 1;
      }
    }
    Logger.writeContent(nm.LOGGER_FILE_NAME, " the predecessor is node " + index);
    return index;
  }

  /**
   * 
   */
  public void doallchecks()
  {
    int predecessor = findPredecessor(this.dead_node_number);
    if (nm.rmi_NodeFind(this.dead_node_number).endsWith("V"))
    {
      predecessor = findPredecessor(predecessor);
    }
    Logger.out(" The predecessor node which should rebalance is " + predecessor);
    if (predecessor == nm.actual_node_number)
    {
      //this actual node needs to simulate the re-balancing
      Logger.out(" The actual node - " + nm.actual_node_number +
                 " will simulate the missing node set - " + this.dead_node_number);
      nm.rebalance();
    }
    else
    {
      final int node_to_send = nm.hashhelp.optimalNodeFind(this.my_node_num, predecessor);
      final InterNodeMessage message = new InterNodeMessage(this.my_node_num,
                                                     predecessor, 
                                                     this.my_node_num, 
                                                     node_to_send,
                                                     REBALANCE_METHOD);
      final String node_tosend_str = nm.rmi_NodeFind(node_to_send);
      try
      {
        final RemoteDistributedHashMap to_send =
          (RemoteDistributedHashMap) NodeManager.registry.lookup(node_tosend_str);
        Logger.out(this.my_node_num + " Sending rebalancing message to node- " + 
          node_to_send);
        to_send.send(message);
      }
      catch (final AccessException e)
      {
        
      }
      catch (RemoteException | InterruptedException e)
      {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      catch (NotBoundException e)
      {
        
      }
    }
  }
}
